package com.appshelves.utils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.appshelves.Settings;
import com.appshelves.model.App;

public class Utils {

    public static interface WriteOperation {
        public void run(DataOutputStream stream) throws IOException;

        public void onFail();
    }

    public static interface ReadOperation {
        public void run(DataInputStream stream) throws IOException;

        public void onFail();
    }

    private static final String COMMON_TAG = "AppShelves";

    private static Context context;

    public static Context getContext() {
        return context;
    }

    public static Activity getActivity() {
        return (Activity) context;
    }

    public static void setContext(Context context) {
        Utils.context = context;
    }

    public static void setContextIfAbsent(Context context) {
        if (getContext() != null)
            return;
        setContext(context);
    }

    /// File IO

    public static boolean readFromFile(String fileName, ReadOperation operation) {
        boolean result = false;
        DataInputStream in = null;
        try {
            FileInputStream fis = getContext().openFileInput(fileName);
            in = new DataInputStream(new BufferedInputStream(fis));
            result = readFromStream(in, operation);
        } catch (FileNotFoundException e) {
            operation.onFail();
        } finally {
            if (in != null)
                try {
                    in.close();
                } catch (IOException e) {
                    operation.onFail();
                    result = false;
                }
        }
        return result;
    }

    public static boolean readFromStream(DataInputStream in, ReadOperation operation) {
        boolean result = true;
        try {
            operation.run(in);
        } catch (EOFException e) { // this is a valid situation.
        } catch (IOException e) {
            operation.onFail();
            result = false;
        }
        return result;
    }

    public static void writeToFile(String fileName, WriteOperation operation) {
        writeToFile(getContext(), fileName, Context.MODE_PRIVATE, operation);
    }

    public static void appendFile(Context context, String fileName, WriteOperation operation) {
        writeToFile(context, fileName, Context.MODE_APPEND, operation);
    }

    private static void writeToFile(Context context, String fileName, int mode, WriteOperation operation) {
        DataOutputStream out = null;
        try {
            FileOutputStream fos = context.openFileOutput(fileName, mode);
            out = new DataOutputStream(new BufferedOutputStream(fos));
            writeToStream(out, operation);
        } catch (FileNotFoundException e) {
            operation.onFail();
        } finally {
            if (out != null)
                try {
                    out.close();
                } catch (IOException e) {
                    operation.onFail();
                }
        }
    }

    public static void writeToStream(DataOutputStream out, WriteOperation operation) {
        try {
            operation.run(out);
        } catch (IOException e) {
            operation.onFail();
        }

    }

    public static void eraseData() {
        File dir = getFilesDir();
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (String file : children) {
                new File(dir, file).delete();
            }
        }
    }

    public static void eraseDataFile(String fileName) {
        File dataFile = new File(getFilesDir(), fileName);
        if (dataFile.exists()) {
            dataFile.delete();
            log("deleted file " + fileName);
        }
    }

    private static File getFilesDir() {
        return getContext().getFilesDir();
    }

    /// Bitmap generation.

    private static Point calcIconSize(Bitmap icon) {
        int w = icon.getWidth(), h = icon.getHeight();
        float sw = (float) w / Settings.iconWidth, sh = (float) h / Settings.iconHeight;
        Point size = new Point();
        if (sw > sh) {
            size.x = Settings.iconWidth;
            size.y = (int) (h / sw);
        } else {
            size.x = (int) (w / sh);
            size.y = Settings.iconHeight;
        }
        return size;
    }

    public static Bitmap createSmallTexture(Bitmap icon, String label) {
        //        Profiler pp = new Profiler(Utils.class, "tex for " + label + " w: " + icon.getWidth());
        Bitmap smallTexture = Bitmap.createBitmap(Settings.getCellContentWidth(), Settings.getCellContentHeight(),
            Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(smallTexture);

        Point iconSize = calcIconSize(icon);
        int x = (smallTexture.getWidth() - iconSize.x) / 2, y = 0;
        Rect iconRect = new Rect(x, y, x + iconSize.x, y + iconSize.y);
        canvas.drawBitmap(icon, null, iconRect, null);

        TextPaint paint = new TextPaint();
        paint.setTextSize(Settings.textSize);
        paint.setAntiAlias(true);
        paint.setColor(Color.WHITE);
        CharSequence labelToDraw = TextUtils.ellipsize(label, paint, Settings.getCellContentWidth(),
            TextUtils.TruncateAt.END);
        Rect textBounds = new Rect();
        paint.getTextBounds(labelToDraw.toString(), 0, labelToDraw.length(), textBounds);
        canvas.drawText(labelToDraw.toString(), (Settings.getCellContentWidth() - textBounds.width()) / 2,
            smallTexture.getHeight() - 1 - Settings.textBottom, paint);

        //        String launchCount = "293";
        //        paint.setShadowLayer(5, 1, 1, Color.BLACK);
        //        paint.getTextBounds(launchCount.toString(), 0, launchCount.length(), textBounds);
        //
        //        NinePatchDrawable npd = (NinePatchDrawable) getActivity().getResources().getDrawable(
        //            R.drawable.number_background);
        //        Rect npdBounds = new Rect(textBounds.left - 1, textBounds.top - 1, textBounds.right + 1, textBounds.bottom + 1);
        //        npdBounds.offsetTo(Settings.getCellContentWidth() - textBounds.width() - 2, 0);
        //        npd.setBounds(npdBounds);
        //        npd.draw(canvas);
        //
        //        canvas.drawText(launchCount, Settings.getCellContentWidth() - textBounds.width() - 2, Settings.textSize -
        //            Settings.textBottom + 1, paint);
        //        pp.e();
        return smallTexture;
    }

    ///

    public static void showAppToast(int resID, App app) {
        Toast.makeText(getContext(), getContext().getString(resID, app.getLabel()), Toast.LENGTH_LONG).show();
    }

    /// logging TODO remove logging

    public static void log(String tag, String msg) {
        Log.i(COMMON_TAG + " " + tag, msg);
    }

    public static void log(String msg) {
        log("", msg);
    }

    public static void log(Object obj, String msg) {
        log(obj.getClass().getSimpleName(), msg);
    }

}
